线上问题,事务互锁,不能操作数据库

线上问题,事务互锁,不能操作数据库

上线一个支付功能,由于用户量过大,需要频繁的获取未支付订单的所有状态
结果频繁报错

1
2
3
4
5
6
7
Caused by: com.alibaba.druid.pool.GetConnectionTimeoutException: wait millis 60000, active 20, maxActive 20, creating 0, runningSqlCount 8 : SELECT  id,pay_code,yaoma,manager,manager_id,paid_cash_all,pay_name,pay_tel,pay_id,pay_bh,pay_datetime,pay_method,manually_add_user,manually_add_user_id,manually_add_datetime,pay_state,openid,le_shua_pay_state,paychanel_order_id,sign_type,out_trade_no,create_time,create_user,update_time,update_user  FROM my_payment_order 

WHERE (pay_state = ? AND out_trade_no IS NOT NULL)
runningSqlCount 5 : SELECT id,pay_code,yaoma,manager,manager_id,paid_cash_all,pay_name,pay_tel,pay_id,pay_bh,pay_datetime,pay_method,manually_add_user,manually_add_user_id,manually_add_datetime,pay_state,openid,le_shua_pay_state,paychanel_order_id,sign_type,out_trade_no,create_time,create_user,update_time,update_user FROM my_payment_order

WHERE (yaoma = ? AND pay_state = ?) ORDER BY pay_datetime ASC
runningSqlCount 6 : UPDATE my_payment_order SET pay_code=?,

一开始以为是数据库链接池不对,增大了链接池,结果不生效

后来查到原因是查询支付接口订单的时候只能单个订单查询,调用外部支付接口的支付订单信息

注意,此处开启了事务,但是订单很多,循环调用很多次外部接口,查询订单信息,若同时并发此处会造成事务死锁

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
/**
* 查询支付中的订单信息,查询乐刷中的订单信息,并且更新相关状态;
* @param list
* @return
*/
@Override
@DataSource(name = "DB1")
@Transactional(rollbackFor = Exception.class)
public List<myPaymentOrder> searchPayWaitingFromLeShua(List<myPaymentOrder> list) throws Exception{
for( myPaymentOrder myPaymentOrderOld:list){
String outTradeNo = myPaymentOrderOld.getOutTradeNo();
String terminalTrace = String.valueOf(myPaymentOrderOld.getPayCode());
//这里调用外部接口
String result= leShuaPayService.leShuaPayQuerypay(outTradeNo,terminalTrace);
JSONObject jsonObject = JSONObject.parseObject(result);
if(ObjectUtil.isNotEmpty(jsonObject)&&ObjectUtil.isNotEmpty(jsonObject.get("result_code"))){
String resultCode = String.valueOf(jsonObject.get("result_code"));
//支付订单成功 01
myPaymentOrderOld.setLeShuaPayState(resultCode);
if(resultCode.equals(LeShuaPayStateEnum.PAY_SUCCESS.getCode())){
myPaymentOrderOld.setPayState(PayStateEnum.PAY_SUCCESS.getCode());
}
//支付失败 02
if(resultCode.equals(LeShuaPayStateEnum.PAY_FAILURE.getCode())){
myPaymentOrderOld.setPayState(PayStateEnum.PAY_ERR.getCode());
}
//支付中 03
if(resultCode.equals(LeShuaPayStateEnum.PAY_WAITING.getCode())){
myPaymentOrderOld.setPayState(PayStateEnum.PAY_WAITING.getCode());
}
// 根据订单号查询,乐刷订单支付状态返回乐刷的支付状态,更新支付人信息,支付状态信息等
this.update(myPaymentOrderOld);
//根据订单的信息,修改订单详情的支付状态;
this.myPaymentOrderDetailsService.updateMyPaymentOrder(myPaymentOrderOld);
}
}
return list;
}

```
修改后

```java

@Override
@DataSource(name = "DB1")
@Transactional(rollbackFor = Exception.class)
public List<myPaymentOrder> searchPayWaitingFromLeShua() throws Exception {
//构造条件
LambdaQueryWrapper<myPaymentOrder> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(myPaymentOrder::getPayState,PayStateEnum.PAY_WAITING.getCode());
queryWrapper.isNotNull(myPaymentOrder::getOutTradeNo);
List<myPaymentOrder> list = this.list(queryWrapper);
for( myPaymentOrder myPaymentOrderOld:list){
String outTradeNo = myPaymentOrderOld.getOutTradeNo();
String terminalTrace = String.valueOf(myPaymentOrderOld.getPayCode());
String result= leShuaPayService.leShuaPayQuerypay(outTradeNo,terminalTrace);
JSONObject jsonObject = JSONObject.parseObject(result);
if(ObjectUtil.isNotEmpty(jsonObject)&&ObjectUtil.isNotEmpty(jsonObject.get("result_code"))){
String resultCode = String.valueOf(jsonObject.get("result_code"));
//支付订单成功 01
myPaymentOrderOld.setLeShuaPayState(resultCode);
if(resultCode.equals(LeShuaPayStateEnum.PAY_SUCCESS.getCode())){
myPaymentOrderOld.setPayState(PayStateEnum.PAY_SUCCESS.getCode());
}
//支付失败 02
if(resultCode.equals(LeShuaPayStateEnum.PAY_FAILURE.getCode())){
myPaymentOrderOld.setPayState(PayStateEnum.PAY_ERR.getCode());
}
//支付中 03
if(resultCode.equals(LeShuaPayStateEnum.PAY_WAITING.getCode())){
myPaymentOrderOld.setPayState(PayStateEnum.PAY_WAITING.getCode());
}
}
}
if(list.size()>0){
// 批量更新状态信息
this.updateBatchById(list);
this.myPaymentOrderDetailsService.updateMyPaymentOrderList(list);
}
return list;
}

更新前的单数据更新sql

1
2
3
4
5
<update id="updateMyPaymentOrder" parameterType="com.halfsky.my.modular.paymentorder.entity.myPaymentOrder" >
update my_payment_order_details
set pay_state = #{paramCondition.payState,jdbcType=INTEGER}
where pay_code = #{paramCondition.payCode,jdbcType=BIGINT}
</update>

更新后批量数据更新sql

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<!-- 批量更新 -->
<update id="updateMyPaymentOrderList" parameterType="java.util.List">
update my_payment_order_details
<trim prefix="set" suffixOverrides=",">
<trim prefix="pay_state =case" suffix="end,">
<foreach collection="paramCondition" item="item" index="index">
when pay_code=#{item.payCode} then #{item.payState}
</foreach>
</trim>
</trim>
where pay_code in
<foreach collection="paramCondition" index="index" item="item" separator="," open="(" close=")">
#{item.payCode}
</foreach>
</update>

总结

单个方法中对多个表的数据修改,编辑,删除数据都要增加事务注解(@Transactional(rollbackFor = Exception.class))
在方法中尽可能的减少对数据库的占用时间,特别是需要调用外部接口之后的修改,建议批量修改,而不是单个修改。
多次的操纵单次修改,在并发量很大的情况下容易造成事务的互锁。

一辈子很短,努力的做好两件事就好;
第一件事是热爱生活,好好的去爱身边的人;
第二件事是努力学习,在工作中取得不一样的成绩,实现自己的价值,而不是仅仅为了赚钱;

继开 wechat
欢迎加我的微信,共同交流技术